/**
 *
 */
package internal.reflect;

import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;

import org.lightcommons.util.ReflectionUtils;
import org.lightmtv.config.Invoker;
import org.lightmtv.config.TypeConverter;

/**
 * @author GL
 *
 * @since 2007-12-21
 */
public class DefaultInvoker implements Invoker{

	private TypeConverter typeConverter ;

	public DefaultInvoker(TypeConverter typeConverter) {
		super();
		this.typeConverter = typeConverter;
	}

	public Type getType(String expression, Type topType) {
		Object[] valueAndType = getValueAndType(null,topType,expression,false);
		return (Type) valueAndType[1];
	}

	public Type getType(String expression, Map ctx, Object root) {
//		return (Type) getTypeAndValue(expression, ctx, root)[0];
		return (Type) getValueAndType(root, ctx, expression, false)[1];
	}

	public Object getValue(String expression, Map ctx, Object root) {
//		return getTypeAndValue(expression, ctx, root)[1];
		return getValueAndType(root, ctx, expression, false)[0];
	}

	private Object[] getValueAndType(Object root, Map ctx,String expression,boolean create ){
		if(expression==null||expression.length()==0)
			return new Object[]{};
		String[] tree = expression.split("\\.");
		Object[] vt  = new Object[2];
		vt[0] = root;
		vt[1] = root==null?null:root.getClass();
		if(!"top".equals(tree[0])){
			if(root!=null){
				vt = getValueAndType(root, root.getClass(), tree[0], create);
			}
			else if(ctx != null){
				vt = getValueAndType(ctx, ctx.getClass(), tree[0], create);
			}else{
				return new Object[]{null,null};
			}
		}
		for(int i=1;i<tree.length;i++){
			vt = getValueAndType(vt[0], (Type)vt[1], tree[i], create);
		}
		return vt;
	}

	private Object[] getValueAndType(Object top,Type topType,String field,boolean create){
		if(topType == null)
			return new Object[]{null,null};//TODO: throw... a exception... Not set... or return false;

		Object value = ReflectionUtils.get(top, field);
		Type fieldType = ReflectionUtils.getFieldType(topType, field);
		if(value==null){
			if(create){
				if(fieldType == null)
					return new Object[]{null,null};
				value = ReflectionUtils.newInstance(fieldType);
				if(!ReflectionUtils.set(top, field, value))
					return new Object[]{null,null};
			}
		}
		//add at 2009-1-15, Collection type should be recreate.
		//集合类型需要重新创建,因为对集合的修改是对整个集合内容的修改，所以必须重建
		else if(create == true && fieldType!=null && Collection.class.isAssignableFrom(ReflectionUtils.getRawClass(fieldType))){
			value = ReflectionUtils.newInstance(fieldType);
			if(!ReflectionUtils.set(top, field, value))
				return new Object[]{null,null};
		}
		else{
			if(fieldType.equals(Object.class))
				fieldType = value.getClass();
		}
		return new Object[]{value,fieldType};
	}
	public void setValue(String expression, Map ctx, Object root, Object value) {
		int index = expression.lastIndexOf(".");
		Object top = root== null ? ctx : root;
		Object[] vt = new Object[]{top,top.getClass()};
		String field = expression;
		if(index > 0){
			String perfEx = expression.substring(0,index);
			field = expression.substring(index+1);
			vt = getValueAndType(root, ctx, perfEx, true);
		}
		Type topType = ReflectionUtils.getFieldType((Type) vt[1], field);
		if(topType == null)
			return;//TODO: throw... a exception... Not set... or return false;
		Object v = typeConverter.convert(value, topType);
		ReflectionUtils.set(vt[0], field, v);
	}

	/*
	public void setValue(String expression, Map ctx, Object root, Object value) {
		if(expression==null||expression.length()==0)
			return;
		String[] tree = expression.split("\\.");
		Object top = root;
		Type topType = root==null?null:root.getClass();
		if(!"top".equals(tree[0]) && tree.length!=1){
			if(top!=null){
				Object field = ReflectUtil.get(top, tree[0]);
				topType = ReflectUtil.getFieldType(topType, tree[0]);
				if(field!=null){
					top = field;
					if(topType==null)
						topType = field.getClass();
				}else if(topType!=null){
					Object obj = ReflectUtil.newInstance(topType);
					ReflectUtil.set(top, tree[0], obj);
					top = obj;
				}else{
					return;
				}
			}
			if(top == null && ctx != null){
				Object field = ctx.get(tree[0]);
				topType = ReflectUtil.getFieldType(ctx.getClass(), tree[0]);
				if(field != null){
					top = field;
					if(topType==null)
						topType = field.getClass();
				}else{
					//TODO: not only String key support
					top = ReflectUtil.newInstance(topType);
					ctx.put(tree[0], top);
				}
			}
			if(top==null)
				return;//TODO: throw... a exception... Not set... or return false;
		}

		for(int i=1;i<tree.length-1;i++){
			if(topType == null)
				return;//TODO: throw... a exception... Not set... or return false;

			Object field = ReflectUtil.get(top, tree[i]);
			if(field==null){
				topType = ReflectUtil.getFieldType(topType, tree[i]);
				field = ReflectUtil.newInstance(topType);
				if(top==null || !ReflectUtil.set(top, tree[i], field)){
					return;//TODO: throw... a exception... Not set... or return false;
				}
				top = field;
			}else{
				top = field;
				topType = top.getClass().equals(Object.class)?
						ReflectUtil.getFieldType(topType, tree[i]):top.getClass();
			}

		}

		topType = ReflectUtil.getFieldType(topType, tree[tree.length-1]);
		if(topType == null)
			return;//TODO: throw... a exception... Not set... or return false;
		Object v = typeConverter.convert(value, topType);
		ReflectUtil.set(top, tree[tree.length-1], v);
	}

	private Object[] getTypeAndValue(String expression, Map ctx, Object root) {
		if(expression==null||expression.length()==0)
			return null;
		//fieldType,valueType,value
		String[] tree = expression.split("\\.");
		Object top = root;
		Type topType = root==null?(ctx==null?null:ctx.getClass()):root.getClass();

		if(!"top".equals(tree[0])){
			if(top!=null){
				top = ReflectUtil.get(top, tree[0]);
			}
			if(top==null && ctx !=null){
				top= ctx.get(tree[0]);
			}
			//getType
			if(top!=null){
				topType = top.getClass();
			}else{
				topType = ReflectUtil.getFieldType(topType, tree[0]);
			}
		}

		for(int i=1;i<tree.length;i++){
			if(top!=null){
				top = ReflectUtil.get(top, tree[i]);
			}
//			getType
			if(top!=null){
				topType = top.getClass();
			}else{
				topType = ReflectUtil.getFieldType(topType, tree[0]);
			}
		}
		return new Object[]{topType,top};
	}

	/**
	 * @return the typeConverter
	 */
	public TypeConverter getTypeConverter() {
		return typeConverter;
	}

	/**
	 * @param typeConverter the typeConverter to set
	 */
	public void setTypeConverter(TypeConverter typeConverter) {
		this.typeConverter = typeConverter;
	}

}
